import com.intellij.notification.*;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;

/**
 * @description: 一键删除没有调用的方法
 * @author: chengsheng@qbb6.com
 * @date: 2018/11/24
 */
public class RmUnusedMethod extends AnAction {


    @Override
    public void actionPerformed(AnActionEvent e) {
        Editor editor = (Editor) e.getDataContext().getData(CommonDataKeys.EDITOR);
        PsiFile psiFile = (PsiFile) e.getDataContext().getData(CommonDataKeys.PSI_FILE);
        Project project = editor.getProject();
        PsiElement referenceAt = psiFile.findElementAt(editor.getCaretModel().getOffset());
        PsiClass selectedClass = (PsiClass) PsiTreeUtil.getContextOfType(referenceAt, new Class[]{PsiClass.class});
        NotificationGroup notificationGroup = null;
        notificationGroup = new NotificationGroup("RmUnusedMethod.NotificationGroup", NotificationDisplayType.BALLOON, true);
        if (selectedClass == null) {
            Notification warn = notificationGroup.createNotification("please open target class", NotificationType.WARNING);
            Notifications.Bus.notify(warn, project);
        } else {
            CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<String>();
            CountDownLatch countDownLatch = new CountDownLatch(1);
            WriteCommandAction.runWriteCommandAction(project, () -> {
                PsiMethod[] psiMethods = selectedClass.getMethods();
                List<PsiMethod> psiMethodList = new ArrayList<>(Arrays.asList(psiMethods));
                if (psiMethodList.size() > 0) {
                    boolean needDel;
                    Iterator<PsiMethod> iterable = psiMethodList.iterator();
                    do {
                        needDel = false;
                        while (iterable.hasNext()) {
                            PsiMethod psiMethod = (PsiMethod) iterable.next();
                            Long psiMethodReferncesCount= MethodReferencesSearch.search(psiMethod).findAll().stream().count();
                            MethodSignatureBackedByPsiMethod methodSignatureBackedByPsiMethod=SuperMethodsSearch.search(psiMethod,selectedClass,true,true).findFirst();
                            PsiMethod superPsiMethod=null;
                            Long superPsiMethodRefernecesCount=null;
                            if(Objects.nonNull(methodSignatureBackedByPsiMethod)){
                                superPsiMethod=methodSignatureBackedByPsiMethod.getMethod();
                                superPsiMethodRefernecesCount=MethodReferencesSearch.search(superPsiMethod).findAll().stream().count();
                            }
                            if((psiMethodReferncesCount==0 && Objects.isNull(superPsiMethod)) ||(psiMethodReferncesCount==0 && Objects.nonNull(superPsiMethod) && superPsiMethodRefernecesCount==0)){
                                iterable.remove();
                                copyOnWriteArrayList.add(psiMethod.getName());
                                psiMethod.delete();
                                if(Objects.nonNull(superPsiMethod)) {
                                    superPsiMethod.delete();
                                }
                                needDel = true;
                            }
                        }
                        if (needDel) {
                            iterable = psiMethodList.iterator();
                        }
                    } while (needDel);
                }
                countDownLatch.countDown();
            });

            try {
                countDownLatch.await();
                Notification info = null;
                if (copyOnWriteArrayList.size() > 0) {
                    info = notificationGroup.createNotification("Success remove:" + copyOnWriteArrayList, NotificationType.INFORMATION);
                } else {
                    info = notificationGroup.createNotification("No method unused!", NotificationType.INFORMATION);
                }
                Notifications.Bus.notify(info, project);
            } catch (InterruptedException e1) {
                Notification error = notificationGroup.createNotification("fail exception:" + e1, NotificationType.ERROR);
                Notifications.Bus.notify(error, project);
                Notification warn = notificationGroup.createNotification("Plugin has some error,but Success remove:" + copyOnWriteArrayList, NotificationType.WARNING);
                Notifications.Bus.notify(warn, project);
            }
        }
    }
}
